// ======================================
//  19.8 类型分类
// ======================================
类型分类是模板元编程中的一项基础技术, 允许我们在编译期识别和区分不同种类的类型。《C++ Templates》第19章的19.8节详细介绍了如何实现各种类型分类特征。

// =================================================
// 19.8.1 确定基本类型（Determining Fundamental Types）
// =================================================
基本类型包括整数、浮点数、字符、布尔值和空类型等C++内置类型。

1.实现原理
// 主模板（默认为false）
template<typename T>
struct IsIntegral : std::false_type {};

// 为每个整型类型提供特化
template<> struct IsIntegral<bool> : std::true_type {};
template<> struct IsIntegral<char> : std::true_type {};
template<> struct IsIntegral<signed char> : std::true_type {};
template<> struct IsIntegral<unsigned char> : std::true_type {};
template<> struct IsIntegral<short> : std::true_type {};
template<> struct IsIntegral<unsigned short> : std::true_type {};
template<> struct IsIntegral<int> : std::true_type {};
template<> struct IsIntegral<unsigned int> : std::true_type {};
template<> struct IsIntegral<long> : std::true_type {};
template<> struct IsIntegral<unsigned long> : std::true_type {};
template<> struct IsIntegral<long long> : std::true_type {};
template<> struct IsIntegral<unsigned long long> : std::true_type {};

2.处理CV限定符
为了正确处理const和volatile限定的类型, 需要先移除这些限定符: 
template<typename T>
struct IsIntegral : IsIntegral<typename std::remove_cv<T>::type> {};

3.组合基本类型特征
可以组合多个基本类型特征创建更广泛的分类: 
template<typename T>
struct IsArithmetic : std::conditional_t<
    IsIntegral<T>::value || IsFloatingPoint<T>::value,
    std::true_type,
    std::false_type
> {};

// ====================================================
// 19.8.2 确定复合类型（Determining Compound Types）
// ====================================================
复合类型包括指针、引用、数组和枚举等由基本类型构建的类型。
1.指针类型检测
// 主模板
template<typename T>
struct IsPointer : std::false_type {};

// 针对指针类型的特化
template<typename T>
struct IsPointer<T*> : std::true_type {};

// 处理CV限定符
template<typename T>
struct IsPointer<T* const> : std::true_type {};
template<typename T>
struct IsPointer<T* volatile> : std::true_type {};
template<typename T>
struct IsPointer<T* const volatile> : std::true_type {};


2.引用类型检测
// 主模板
template<typename T>
struct IsReference : std::false_type {};

// 左值引用特化
template<typename T>
struct IsReference<T&> : std::true_type {};

// 右值引用特化
template<typename T>
struct IsReference<T&&> : std::true_type {};

3.数组类型检测
// 主模板
template<typename T>
struct IsArray : std::false_type {};

// 未知边界数组特化
template<typename T>
struct IsArray<T[]> : std::true_type {};

// 已知边界数组特化
template<typename T, std::size_t N>
struct IsArray<T[N]> : std::true_type {};


// ============================================
// 19.8.3 识别函数类型（Recognizing Function Types）
// ============================================
函数类型包括普通函数、成员函数和函数指针等。
1.普通函数检测
// 检查是否为函数类型
R: 返回类型
Args... 是函数参数类型的参数包（可变参数模板）
R() 表示一个无参数、返回R类型的函数类型
R(Args...) 表示一个接受Args...参数、返回R类型的函数类型

// 主模板
template<typename T>
struct IsFunction : std::false_type {};

// 无参数函数特化
template<typename R>
struct IsFunction<R()> : std::true_type {};

// 有参数函数特化
template<typename R, typename... Args>
struct IsFunction<R(Args...)> : std::true_type {};

// 可变参数函数特化
template<typename R, typename... Args>
struct IsFunction<R(Args..., ...)> : std::true_type {};

2.成员函数检测
R(C::*)(Args...) 是指向C类成员函数的指针类型, 该函数接受Args...参数并返回R类型
在C++中, 成员函数指针的声明语法是: 
返回类型 (类名::*指针名)(参数列表)

// 成员函数特化
template<typename R, typename C, typename... Args>
struct IsFunction<R(C::*)(Args...)> : std::true_type {};

// const成员函数特化
template<typename R, typename C, typename... Args>
struct IsFunction<R(C::*)(Args...) const> : std::true_type {};

// volatile成员函数特化
template<typename R, typename C, typename... Args>
struct IsFunction<R(C::*)(Args...) volatile> : std::true_type {};

// const volatile成员函数特化
template<typename R, typename C, typename... Args>
struct IsFunction<R(C::*)(Args...) const volatile> : std::true_type {};


// ===========================================
// 19.8.4 确定类类型（Determining Class Types）
// ===========================================
类类型包括类、结构体和联合体。

1.类类型检测的实现
// 辅助模板: 两个不同大小的类型
template<std::size_t> struct Rank {};
Rank<0> detect(...);  // 变参函数, 匹配任何类型

// 只匹配可以进行地址操作的类型（类类型）
template<typename T>
Rank<1> detect(T*, T&);

// 主模板
template<typename T>
struct IsClass {
    // 尝试对T*和T&调用detect, 看返回值大小
    static constexpr bool value = 
        sizeof(detect(static_cast<T*>(nullptr), std::declval<T&>())) == sizeof(Rank<1>);
};

2.使用SFINAE的替代实现
template<typename T, typename = void>
struct IsClass : std::false_type {};

template<typename T>
struct IsClass<T, std::void_t<int T::*>> : std::true_type {};

// 这个实现利用了只有类类型才能有成员指针的特性。

// ====================================================
// 19.8.5 确定枚举类型（Determining Enumeration Types）
// ====================================================
枚举类型包括传统枚举和C++11引入的强类型枚举。

1.枚举类型检测
// 使用排除法: 不是其他已知类型的整型兼容类型就是枚举
template<typename T>
struct IsEnum {
    static constexpr bool value = 
        std::is_integral<T>::value &&
        !std::is_class<T>::value &&
        !std::is_union<T>::value &&
        !std::is_array<T>::value &&
        !std::is_reference<T>::value &&
        !std::is_pointer<T>::value &&
        !std::is_member_pointer<T>::value &&
        !std::is_arithmetic<T>::value &&
        !std::is_void<T>::value &&
        !std::is_function<T>::value;
};

2.C++11后的实现
在C++11及以后的标准中, 编译器提供了内置支持, 使得枚举检测更加简单和可靠: 

// 使用编译器内置支持
template<typename T>
struct IsEnum {
    static constexpr bool value = __is_enum(T);
};

// 总结
类型分类特征是C++模板元编程的基础工具, 它们允许我们在编译期识别和区分不同种类的类型。通过组合这些基本特征, 我们可以构建更复杂的类型操作和条件逻辑, 实现高度泛型和优化的代码。

